﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace 枸杞VBE插件
{
    internal class 破解工程密码 : IDisposable
    {
        private delegate int 替代函数入口的委托(int hInstance, int pTemplateName, int hWndParent, int lpDialogFunc, int dwInitParam);

        private 替代函数入口的委托 替代函数入口的委托实例;
        private ApiHook ApiHook;
        private bool disposedValue;

        private int 替代的新函数(int hInstance, int pTemplateName, int hWndParent, int lpDialogFunc, int dwInitParam)
        {
            if (pTemplateName == 4070) return 1;//有程序调用DialogBoxParamA装入4070号对话框，这里就我们直接返回1，让VBE以为密码正确了
            ApiHook.UnHook(); //有程序调用DialogBoxParamA，但装入的不是4070号对话框，这里恢复原来函数的功能，再进行原来的函数
            dynamic ret = WinApi.DialogBoxParam(hInstance, pTemplateName, hWndParent, lpDialogFunc, dwInitParam);
            ApiHook.Hook();//原来的函数执行完毕，再次hook
            return ret;
        }

        public void 无视工程密码开启()
        {
            if (ApiHook == null) ApiHook = new ApiHook();
            if (ApiHook.已安装) ApiHook.卸载();
            替代函数入口的委托实例 = new 替代函数入口的委托(替代的新函数);
            ApiHook.安装("user32.dll", "DialogBoxParamA", Marshal.GetFunctionPointerForDelegate(替代函数入口的委托实例));
            ApiHook.Hook();
        }

        public void 无视工程密码关闭()
        {
            ApiHook.卸载();
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (ApiHook.已安装) ApiHook.卸载();
                    ApiHook = null;
                    替代函数入口的委托实例 = null;
                    // TODO: 释放托管状态(托管对象)
                }
                // TODO: 释放未托管的资源(未托管的对象)并重写终结器
                // TODO: 将大型字段设置为 null
                disposedValue = true;
            }
        }

        // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器
        // ~破解工程密码()
        // {
        //     // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
        //     Dispose(disposing: false);
        // }
        public void Dispose()
        {
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }

    public class ApiHook
    {
        //内存保护属性
        private const int PAGE_EXECUTE_READWRITE = 0x40;

        //api函数地址
        private IntPtr 旧函数指针;

        //原始内存保护属性
        private int 原始内存保护属性 = 0;

        //原始入口点数据
        private IntPtr 旧入口;

        //新入口点数据
        private IntPtr 新入口;

        private bool 是64位环境;
        public bool 已安装 { get; private set; }

        public ApiHook()
        {
        }

        public ApiHook(string 模块名, string 函数名, IntPtr 新函数指针)
        {
            安装(模块名, 函数名, 新函数指针);
        }

        public bool 安装(string 模块名, string 函数名, IntPtr 新函数指针)
        {
            是64位环境 = IntPtr.Size == 8;
            if (是64位环境)
            {
                旧入口 = Marshal.AllocHGlobal(12);
                新入口 = Marshal.AllocHGlobal(12);
            }
            else
            {
                旧入口 = Marshal.AllocHGlobal(5);
                新入口 = Marshal.AllocHGlobal(5);
            }
            IntPtr 模块指针 = default;
            foreach (ProcessModule 模块 in Process.GetCurrentProcess().Modules)
            {
                if (模块.ModuleName.ToLower() == 模块名.ToLower())//忽略大小写判断名称
                {
                    模块指针 = 模块.BaseAddress;
                    break; // TODO: might not be correct. Was : Exit For
                }
            }
            if (模块指针 == IntPtr.Zero) return false;
            旧函数指针 = WinApi.GetProcAddress(模块指针, 函数名);
            //函数入口
            if (旧函数指针 == IntPtr.Zero) return false;
            if (!WinApi.VirtualProtect(旧函数指针, 1, PAGE_EXECUTE_READWRITE, ref 原始内存保护属性)) return false;
            //修改内存属性可写入内存
            //------------------------------------------在非托管内存构造数据---------------------------------------------
            //读原始字节
            for (int i = 0; i <= (是64位环境 ? 11 : 4); i++)
            {
                Marshal.WriteByte(旧入口, i, Marshal.ReadByte(旧函数指针, i));
            }
            //构造新字节
            if (是64位环境)
            {
                Marshal.WriteByte(新入口, 0, 0x48);
                Marshal.WriteByte(新入口, 1, 0xb8);
                Marshal.WriteByte(新入口, 10, 0x50);
                Marshal.WriteByte(新入口, 11, 0xc3);
                Marshal.WriteInt64(新入口, 2, 新函数指针.ToInt64());
            }
            else
            {
                Marshal.WriteByte(新入口, 0, 0xe9);
                Marshal.WriteInt32(新入口, 1, 新函数指针.ToInt32() - 旧函数指针.ToInt32() - 5);
            }
            //---------------------------------------------数据构造完毕-----------------------------------------------------
            已安装 = true;
            return true;
        }

        public void UnHook()
        {
            if (!已安装)
                return;
            for (int i = 0; i <= (是64位环境 ? 11 : 4); i++)
            {
                Marshal.WriteByte(旧函数指针, i, Marshal.ReadByte(旧入口, i));
            }
        }

        public void Hook()
        {
            if (!已安装)
                return;
            for (int i = 0; i <= (是64位环境 ? 11 : 4); i++)
            {
                Marshal.WriteByte(旧函数指针, i, Marshal.ReadByte(新入口, i));
                //Debug.Print(Marshal.ReadByte(NewEntry, i) & " " & Marshal.ReadByte(OldEntry, i))
            }
        }

        public bool 卸载()
        {
            if (!已安装) return true;
            UnHook();
            WinApi.VirtualProtect(旧函数指针, 1, 原始内存保护属性, ref 原始内存保护属性);
            已安装 = false;
            return true;
        }
    }
}